﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Data.Web;
using IssueVision.Common;
using MVVMPlugin;

namespace IssueVision.ViewModel
{
    [ExportPlugin(ViewModelTypes.UserMaintenanceViewModel, PluginType.ViewModel)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class UserMaintenanceViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private enum UserMaintenanceOperation
        {
            Add,
            Delete,
            Update
        }
        private IAdminIssueVisionModel _adminIssueVisionModel;
        private UserMaintenanceOperation _operation;
        private string _userNameToDisplay;
        #endregion "Private Data Members"

        #region "Constructor"
        public UserMaintenanceViewModel()
        {
            _adminIssueVisionModel = PluginCatalogService.Container.GetExportedValue<IAdminIssueVisionModel>();

            // set up event handling
            _adminIssueVisionModel.GetUsersComplete += _adminIssueVisionModel_GetUsersComplete;
            _adminIssueVisionModel.SaveChangesComplete += _adminIssueVisionModel_SaveChangesComplete;
            _adminIssueVisionModel.PropertyChanged += _adminIssueVisionModel_PropertyChanged;

            // set initial user maintenance operation
            _userNameToDisplay = string.Empty;
            _operation = UserMaintenanceOperation.Update;
            IsUpdateUser = true;
            IsAddUser = false;

            // load all users
            _adminIssueVisionModel.GetUsersAsync();
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_adminIssueVisionModel != null)
            {
                // unregister all events
                _adminIssueVisionModel.GetUsersComplete -= _adminIssueVisionModel_GetUsersComplete;
                _adminIssueVisionModel.SaveChangesComplete -= _adminIssueVisionModel_SaveChangesComplete;
                _adminIssueVisionModel.PropertyChanged -= _adminIssueVisionModel_PropertyChanged;
                _adminIssueVisionModel = null;
            }
            // set properties back to null
            _allUsers = null;
            CurrentUser = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private IEnumerable<User> _allUsers;

        private CollectionViewSource _allUsersSource;

        public CollectionViewSource AllUsersSource
        {
            get { return _allUsersSource; }
            private set
            {
                if (!ReferenceEquals(_allUsersSource, value))
                {
                    _allUsersSource = value;
                    RaisePropertyChanged("AllUsersSource");
                }
            }
        }

        private IEnumerable<string> _userTypeEntries;

        public IEnumerable<string> UserTypeEntries
        {
            get
            {
                if (_userTypeEntries == null)
                {
                    _userTypeEntries = new[]
                    {
                        IssueVisionServiceConstant.UserTypeUser,
                        IssueVisionServiceConstant.UserTypeAdmin
                    };
                }
                return _userTypeEntries;
            }
        }

        private User _currentUser;

        public User CurrentUser
        {
            get { return _currentUser; }
            private set
            {
                if (!ReferenceEquals(_currentUser, value))
                {
                    _currentUser = value;
                    RaisePropertyChanged("CurrentUser");
                }
            }
        }

        private bool _isAddUser;

        public bool IsAddUser
        {
            get { return _isAddUser; }
            private set
            {
                if (value != _isAddUser)
                {
                    _isAddUser = value;
                    RaisePropertyChanged("IsAddUser");
                }
            }
        }

        private bool _isUpdateUser;

        public bool IsUpdateUser
        {
            get { return _isUpdateUser; }
            private set
            {
                if (value != _isUpdateUser)
                {
                    _isUpdateUser = value;
                    RaisePropertyChanged("IsUpdateUser");
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand _addUserCommand;

        public RelayCommand AddUserCommand
        {
            get
            {
                if (_addUserCommand == null)
                {
                    _addUserCommand = new RelayCommand(
                        OnAddUserCommand,
                        () => (_adminIssueVisionModel != null) && !(_adminIssueVisionModel.HasChanges));
                }
                return _addUserCommand;
            }
        }

        private void OnAddUserCommand()
        {
            try
            {
                if (!_adminIssueVisionModel.IsBusy)
                {
                    // cancel any changes before adding a new user
                    if (_adminIssueVisionModel.HasChanges)
                    {
                        _adminIssueVisionModel.RejectChanges();
                    }

                    CurrentUser = _adminIssueVisionModel.AddNewUser();

                    _operation = UserMaintenanceOperation.Add;
                    IsUpdateUser = false;
                    IsAddUser = true;
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<User> _removeUserCommand;

        public RelayCommand<User> RemoveUserCommand
        {
            get
            {
                if (_removeUserCommand == null)
                {
                    _removeUserCommand = new RelayCommand<User>(
                        OnRemoveUserCommand,
                        g => (_adminIssueVisionModel != null) && !(_adminIssueVisionModel.HasChanges) && (g != null));
                }
                return _removeUserCommand;
            }
        }

        private void OnRemoveUserCommand(User g)
        {
            try
            {
                if (!_adminIssueVisionModel.IsBusy)
                {
                    // cancel any changes before deleting a user
                    if (_adminIssueVisionModel.HasChanges)
                    {
                        _adminIssueVisionModel.RejectChanges();
                    }

                    // ask to confirm deleting the current user
                    var dialogMessage = new DialogMessage(
                        this,
                        CommonResources.DeleteCurrentUserMessageBoxText,
                        s =>
                        {
                            if (s == MessageBoxResult.OK)
                            {
                                // if confirmed, removing CurrentUser
                                _adminIssueVisionModel.RemoveUser(g);

                                // cache the current user name as empty string
                                _userNameToDisplay = string.Empty;

                                _operation = UserMaintenanceOperation.Delete;
                                IsUpdateUser = true;
                                IsAddUser = false;

                                _adminIssueVisionModel.SaveChangesAsync();
                            }
                        })
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = CommonResources.ConfirmMessageBoxCaption
                    };

                    AppMessages.PleaseConfirmMessage.Send(dialogMessage);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _submitChangeCommand;

        public RelayCommand SubmitChangeCommand
        {
            get
            {
                if (_submitChangeCommand == null)
                {
                    _submitChangeCommand = new RelayCommand(
                        OnSubmitChangeCommand,
                        () => (_adminIssueVisionModel != null) && (_adminIssueVisionModel.HasChanges));
                }
                return _submitChangeCommand;
            }
        }

        private void OnSubmitChangeCommand()
        {
            try
            {
                if (!_adminIssueVisionModel.IsBusy)
                {
                    if (CurrentUser != null)
                    {
                        // this should trigger validation even if the following field is not changed and is null
                        if (string.IsNullOrWhiteSpace(CurrentUser.Name))
                            CurrentUser.Name = string.Empty;
                        if (string.IsNullOrWhiteSpace(CurrentUser.FirstName))
                            CurrentUser.FirstName = string.Empty;
                        if (string.IsNullOrWhiteSpace(CurrentUser.LastName))
                            CurrentUser.LastName = string.Empty;
                        if (string.IsNullOrWhiteSpace(CurrentUser.Email))
                            CurrentUser.Email = null;
                        if (string.IsNullOrWhiteSpace(CurrentUser.NewPassword))
                            CurrentUser.NewPassword = string.Empty;
                        if (string.IsNullOrWhiteSpace(CurrentUser.NewPasswordConfirmation))
                            CurrentUser.NewPasswordConfirmation = string.Empty;

                        if (CurrentUser.TryValidateProperty("Name") && CurrentUser.TryValidateProperty("FirstName")
                            && CurrentUser.TryValidateProperty("LastName") && CurrentUser.TryValidateProperty("Email")
                            && CurrentUser.TryValidateProperty("NewPassword") && CurrentUser.TryValidateProperty("NewPasswordConfirmation")
                            && CurrentUser.TryValidateProperty("UserType"))
                        {
                            // following settings are there to prevent validation errors
                            CurrentUser.Password = CurrentUser.NewPassword;
                            CurrentUser.PasswordAnswer = "PasswordAnswer";
                            CurrentUser.PasswordAnswerConfirmation = "PasswordAnswer";

                            // cache the current user name
                            _userNameToDisplay = CurrentUser.Name;

                            // change is from User Maintenance screen
                            CurrentUser.IsUserMaintenance = true;

                            _adminIssueVisionModel.SaveChangesAsync();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _cancelChangeCommand;

        public RelayCommand CancelChangeCommand
        {
            get
            {
                if (_cancelChangeCommand == null)
                {
                    _cancelChangeCommand = new RelayCommand(
                        OnCancelChangeCommand,
                        () => (_adminIssueVisionModel != null) && (_adminIssueVisionModel.HasChanges));
                }
                return _cancelChangeCommand;
            }
        }

        private void OnCancelChangeCommand()
        {
            try
            {
                if (!_adminIssueVisionModel.IsBusy)
                {
                    // ask to confirm canceling the current user in edit
                    var dialogMessage = new DialogMessage(
                        this,
                        CommonResources.CancelCurrentUserMessageBoxText,
                        s =>
                        {
                            if (s == MessageBoxResult.OK)
                            {
                                // if confirmed, cancel any change to CurrentUser
                                _adminIssueVisionModel.RejectChanges();
                                if (_operation == UserMaintenanceOperation.Update)
                                {
                                    // if in update operation, just refresh CurrentUser
                                    RaisePropertyChanged("CurrentUser");
                                }
                                else
                                {
                                    // if in add operation, reload all users again
                                    _adminIssueVisionModel.GetUsersAsync();
                                }
                            }
                        })
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = CommonResources.ConfirmMessageBoxCaption
                    };

                    AppMessages.PleaseConfirmMessage.Send(dialogMessage);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        private void _adminIssueVisionModel_GetUsersComplete(object sender, EntityResultsArgs<User> e)
        {
            if (!e.HasError)
            {
                // cancel any changes before setting AllUsersSource
                if (_adminIssueVisionModel.HasChanges)
                {
                    _adminIssueVisionModel.RejectChanges();
                }

                // set AllUsersSource
                _allUsers = e.Results.OrderBy(g => g.Name);

                AllUsersSource = new CollectionViewSource { Source = _allUsers };
                AllUsersSource.View.CurrentChanging += View_CurrentChanging;
                AllUsersSource.View.CurrentChanged += View_CurrentChanged;

                // set CurrentUser
                if (_allUsers.Count() >= 1)
                {
                    if (_allUsers.FirstOrDefault(n => n.Name == _userNameToDisplay) == null)
                    {
                        // set the first row as the current user
                        var enumerator = _allUsers.GetEnumerator();
                        enumerator.MoveNext();
                        CurrentUser = enumerator.Current;
                    }
                    else
                    {
                        CurrentUser = _allUsers.FirstOrDefault(n => n.Name == _userNameToDisplay);
                        AllUsersSource.View.MoveCurrentTo(CurrentUser);
                    }
                }

                // set operation back to update
                _operation = UserMaintenanceOperation.Update;
                IsUpdateUser = true;
                IsAddUser = false; 
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void View_CurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            MessageBoxResult theResult = MessageBoxResult.OK;

            if (_adminIssueVisionModel.HasChanges)
            {
                // ask to confirm canceling the current user in edit
                var dialogMessage = new DialogMessage(
                    this,
                    CommonResources.CancelCurrentUserMessageBoxText,
                    s => theResult = s)
                {
                    Button = MessageBoxButton.OKCancel,
                    Caption = CommonResources.ConfirmMessageBoxCaption
                };

                AppMessages.PleaseConfirmMessage.Send(dialogMessage);

                if (theResult == MessageBoxResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        private void View_CurrentChanged(object sender, EventArgs e)
        {
            // cancel any changes before editing another user
            if (_adminIssueVisionModel.HasChanges)
            {
                _adminIssueVisionModel.RejectChanges();
            }
            // assign the new current item to CurrentUser
            CurrentUser = (User)AllUsersSource.View.CurrentItem;
        }

        private void _adminIssueVisionModel_SaveChangesComplete(object sender, SubmitOperationEventArgs e)
        {
            if (!e.HasError)
            {
                if (_operation == UserMaintenanceOperation.Update)
                {
                    // user update successful
                    var dialogMessage = new DialogMessage(
                        this,
                        CommonResources.UserMaintenanceUpdatedText,
                        null)
                        {
                            Button = MessageBoxButton.OK,
                            Caption = CommonResources.UserMaintenanceUpdatedCaption
                        };

                    AppMessages.StatusUpdateMessage.Send(dialogMessage);
                }

                // reload the user list after add/delete/update
                _adminIssueVisionModel.GetUsersAsync();
            }
            else
            {
                if (e.SubmitOp != null &&
                    (e.SubmitOp.EntitiesInError != null && e.SubmitOp.EntitiesInError.Count() == 1))
                {
                    ValidationResult validationResult = e.SubmitOp.EntitiesInError.First()
                        .ValidationErrors.FirstOrDefault();

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.CannotInsertDuplicateUser))
                    {
                        // no permission to add duplicate user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.CannotInsertDuplicateUser,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        return;
                    }

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.NoPermissionToDeleteUser))
                    {
                        // no permission to delete user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.NoPermissionToDeleteUser,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        // cancel any changes
                        if (_adminIssueVisionModel.HasChanges)
                        {
                            _adminIssueVisionModel.RejectChanges();
                        }
                        return;
                    }

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.CannotDeleteUserAssignedToID))
                    {
                        // delete user failed because there are still issues assigned to this user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.CannotDeleteUserAssignedToID,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        // cancel any changes
                        if (_adminIssueVisionModel.HasChanges)
                        {
                            _adminIssueVisionModel.RejectChanges();
                        }
                        return;
                    }

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.CannotDeleteUserChangedByID))
                    {
                        // delete user failed because there are still issues changed by this user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.CannotDeleteUserChangedByID,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        // cancel any changes
                        if (_adminIssueVisionModel.HasChanges)
                        {
                            _adminIssueVisionModel.RejectChanges();
                        }
                        return;
                    }

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.CannotDeleteUserOpenedByID))
                    {
                        // delete user failed because there are still issues opened by this user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.CannotDeleteUserOpenedByID,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        // cancel any changes
                        if (_adminIssueVisionModel.HasChanges)
                        {
                            _adminIssueVisionModel.RejectChanges();
                        }
                        return;
                    }

                    if (validationResult != null &&
                        string.Equals(validationResult.ErrorMessage, ErrorResources.CannotDeleteUserResolvedByID))
                    {
                        // delete user failed because there are still issues resolved by this user
                        var dialogMessage = new DialogMessage(
                            this,
                            ErrorResources.CannotDeleteUserResolvedByID,
                            null)
                            {
                                Button = MessageBoxButton.OK,
                                Caption = CommonResources.UserMaintenanceErrorCaption
                            };

                        AppMessages.StatusUpdateMessage.Send(dialogMessage);

                        // cancel any changes
                        if (_adminIssueVisionModel.HasChanges)
                        {
                            _adminIssueVisionModel.RejectChanges();
                        }
                        return;
                    }
                }
                // notify user if there is any unexpected error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _adminIssueVisionModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("HasChanges"))
            {
                AddUserCommand.RaiseCanExecuteChanged();
                RemoveUserCommand.RaiseCanExecuteChanged();
                SubmitChangeCommand.RaiseCanExecuteChanged();
                CancelChangeCommand.RaiseCanExecuteChanged();
            }
        }

        #endregion "Private Methods"
    }
}
